#ifndef _IM_SERVER_APNS_MANAGER_
#define _IM_SERVER_APNS_MANAGER_
#include <string>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#include <deque>
#include <vector>
#include <exception>
#include <stdexcept>
#include "base/logging.h"
#include <pthread.h>
#include <unistd.h>
#include <boost/thread.hpp>
//#include <boost/thread/condition.hpp>
#include "base/thread_manager.h"
#include "base/singleton.h"
#include "base/config_reader.h"

namespace imserver {

#define PORT            "2195"
#define APNS_SANDBOX 1
#define SERVER          "gateway.sandbox.push.apple.com"
#define CLIENT          "gateway.sandbox.push.apple.com"
//#define SERVER          "gateway.push.apple.com"
//#define CLIENT          "gateway.push.apple.com"

struct PushMsgEntry {
  int64_t uid;
  std::string tokenStr;
  std::string payload;
};

void init_OpenSSL(void);
unsigned int htod(char c);
unsigned int char2binary(const std::string& c);
std::vector<unsigned> string2binary(std::string s);

using apache::thrift::concurrency::Runnable;

class APNSPusher : public Runnable {
public:
  APNSPusher(const std::string& certfile, const std::string& password) :
    _certfile(certfile), _password(password) {
    _ssl_state = false;
    LOG_INFO("APNSPusher::APNSPusher --> certfile =  " << _certfile <<
      " password = " << _password);
  }
  virtual void run();
  void insertMsg(PushMsgEntry & entry);
  void pushOneMsg(PushMsgEntry & m);
  bool init();
  ~APNSPusher();

private:
  bool _init_connection();
  SSL_CTX * _setup_client_ctx(void);
  int _do_client_loop(SSL *ssl);
  int _sendPayload(SSL *, const std::vector<unsigned> &, const char *, size_t);
  std::deque<PushMsgEntry> _msgQueue;
  
  BIO *_conn;
  SSL *_ssl;
  SSL_CTX *_ctx;
  
  bool _ssl_state;
  std::string _certfile;
  std::string _password;
  apache::thrift::concurrency::Monitor _monitor;
};

typedef APNSPusher* PusherPtr;

class wrong_token_string: public std::runtime_error {
public:
  explicit wrong_token_string(const std::string &s) :
    std::runtime_error(s) {
  }
};

class APNSFeedback : public Runnable {
public:
  APNSFeedback(const std::string& certfile, const std::string& password) :
    _certfile(certfile), _password(password) {
    LOG_INFO("APNSFeedback::APNSFeedback --> certfile =  " << _certfile <<
      " password = " << _password);
  }
  virtual void run();
private:
  std::string _certfile;
  std::string _password;
};

class APNSManager : public base::Singleton<APNSManager> {
public:
  static const uint32_t PUSHER_SIZE;
  void pushMsg(int64_t uid, const std::string& token, const std::string& content);

private:
  friend class base::Singleton<APNSManager>;
  APNSManager() {
    /*
    ConfigReader cfg("../conf/push_service.conf");
    std::string pem_file = cfg.Get("APNS", "pem_file");
    std::string passwd = cfg.Get("APNS", "passwd");
    _pusher.resize(PUSHER_SIZE);
    for (size_t i = 0; i < PUSHER_SIZE; ++i) {
      _pusher[i] = new APNSPusher(pem_file, passwd);
      if (_pusher[i]->init()) {
        LOG_INFO("APNSManager::APNSManager --> init APNSPusher ok. index:" << i);
        base::GetThreadManager()->add(boost::shared_ptr<Runnable>(_pusher[i]));
      } else {
        LOG_ERROR("APNSManager::APNSManager --> init APNSPusher failed. index:" << i);
      }
    }

    base::GetThreadManager()->add(boost::shared_ptr<Runnable>(new APNSFeedback(pem_file, passwd)));
    */
  }

  std::vector<PusherPtr> _pusher;
};

}

#endif /* _IM_SERVER_APNS_MANAGER_ */

